/* * Hibernate, Relational Persistence for Idiomatic Java * * License: Apache License, Version 2.0 * See the LICENSE file in the root directory or visit http://www.apache.org/licenses/LICENSE-2.0 */ package org.hibernate.sqm.test.hql; import java.util.List; import java.util.Map; import org.hibernate.query.sqm.StrictJpaComplianceViolation; import org.hibernate.query.sqm.domain.SqmExpressableTypeEntity; import org.hibernate.query.sqm.domain.SqmPluralAttribute; import org.hibernate.query.sqm.domain.SqmPluralAttribute.CollectionClassification; import org.hibernate.query.sqm.domain.SqmPluralAttributeElement.ElementClassification; import org.hibernate.query.sqm.domain.SqmPluralAttributeIndex.IndexClassification; import org.hibernate.query.sqm.tree.SqmQuerySpec; import org.hibernate.query.sqm.tree.SqmSelectStatement; import org.hibernate.query.sqm.tree.expression.BinaryArithmeticSqmExpression; import org.hibernate.query.sqm.tree.expression.domain.AbstractSqmCollectionIndexReference; import org.hibernate.query.sqm.tree.expression.domain.SqmCollectionElementReference; import org.hibernate.query.sqm.tree.expression.domain.SqmEntityReference; import org.hibernate.query.sqm.tree.expression.domain.SqmMapEntryBinding; import org.hibernate.query.sqm.tree.expression.domain.SqmPluralAttributeReference; import org.hibernate.query.sqm.tree.expression.domain.SqmSingularAttributeReference; import org.hibernate.query.sqm.tree.from.SqmFromElementSpace; import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationTarget; import org.hibernate.query.sqm.tree.select.SqmSelection; import org.hibernate.sqm.test.domain.EntityOfMaps; import org.hibernate.sqm.test.domain.StandardModelTest; import org.hibernate.test.sqm.domain.FromElementHelper; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.hamcrest.CoreMatchers; import static org.hamcrest.CoreMatchers.endsWith; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; /** * Test various forms of selections * * @author Steve Ebersole */ public class SelectClauseTests extends StandardModelTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testSimpleAliasSelection() { SqmSelectStatement statement = interpretSelect( "select p from Person p" ); assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() ); SqmSelection selection = statement.getQuerySpec().getSelectClause().getSelections().get( 0 ); assertThat( selection.getExpression(), instanceOf( SqmEntityReference.class ) ); } @Test public void testSimpleAttributeSelection() { SqmSelectStatement statement = interpretSelect( "select p.nickName from Person p" ); assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() ); SqmSelection selection = statement.getQuerySpec().getSelectClause().getSelections().get( 0 ); assertThat( selection.getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); } @Test public void testCompoundAttributeSelection() { SqmSelectStatement statement = interpretSelect( "select p.nickName, p.name.first from Person p" ); assertEquals( 2, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 1 ).getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); } @Test public void testMixedAliasAndAttributeSelection() { SqmSelectStatement statement = interpretSelect( "select p, p.nickName from Person p" ); assertEquals( 2, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( SqmEntityReference.class ) ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 1 ).getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); } @Test public void testSimpleDynamicInstantiationSelection() { SqmSelectStatement statement = interpretSelect( "select new org.hibernate.sqm.test.hql.SelectClauseTests$DTO(p.nickName, p.numberOfToes) from Person p" ); assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( SqmDynamicInstantiation.class ) ); } @Test public void testMultipleDynamicInstantiationSelection() { SqmSelectStatement statement = interpretSelect( "select new org.hibernate.sqm.test.hql.SelectClauseTests$DTO(p.nickName, p.numberOfToes), " + "new org.hibernate.sqm.test.hql.SelectClauseTests$DTO(p.nickName, p.numberOfToes) " + "from Person p" ); assertEquals( 2, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( SqmDynamicInstantiation.class ) ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 1 ).getExpression(), instanceOf( SqmDynamicInstantiation.class ) ); } @Test public void testMixedAttributeAndDynamicInstantiationSelection() { SqmSelectStatement statement = interpretSelect( "select new org.hibernate.sqm.test.hql.SelectClauseTests$DTO(p.nickName, p.numberOfToes), p.nickName from Person p" ); assertEquals( 2, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( SqmDynamicInstantiation.class ) ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 1 ).getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); } @Test public void testNestedDynamicInstantiationSelection() { SqmSelectStatement statement = interpretSelect( "select new org.hibernate.sqm.test.hql.SelectClauseTests$DTO(" + " p.nickName, " + " p.numberOfToes, " + " new org.hibernate.sqm.test.hql.SelectClauseTests$DTO(p.nickName, p.numberOfToes) " + " ) " + "from Person p" ); assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( SqmDynamicInstantiation.class ) ); SqmDynamicInstantiation dynamicInstantiation = (SqmDynamicInstantiation) statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(); assertThat( dynamicInstantiation.getInstantiationTarget().getNature(), equalTo( SqmDynamicInstantiationTarget.Nature.CLASS ) ); assertThat( dynamicInstantiation.getInstantiationTarget().getJavaType(), CoreMatchers.<Class>equalTo( DTO.class ) ); assertEquals( 3, dynamicInstantiation.getArguments().size() ); assertThat( dynamicInstantiation.getArguments().get( 0 ).getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); assertThat( dynamicInstantiation.getArguments().get( 1 ).getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); assertThat( dynamicInstantiation.getArguments().get( 2 ).getExpression(), instanceOf( SqmDynamicInstantiation.class ) ); SqmDynamicInstantiation nestedInstantiation = (SqmDynamicInstantiation) dynamicInstantiation.getArguments().get( 2 ).getExpression(); assertThat( nestedInstantiation.getInstantiationTarget().getNature(), equalTo( SqmDynamicInstantiationTarget.Nature.CLASS ) ); assertThat( nestedInstantiation.getInstantiationTarget().getJavaType(), CoreMatchers.<Class>equalTo( DTO.class ) ); } @Test public void testSimpleDynamicListInstantiation() { SqmSelectStatement statement = interpretSelect( "select new list(p.nickName, p.numberOfToes) from Person p" ); assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( SqmDynamicInstantiation.class ) ); SqmDynamicInstantiation instantiation = (SqmDynamicInstantiation) statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(); assertThat( instantiation.getInstantiationTarget().getNature(), equalTo( SqmDynamicInstantiationTarget.Nature.LIST ) ); assertThat( instantiation.getInstantiationTarget().getJavaType(), CoreMatchers.<Class>equalTo( List.class ) ); assertEquals( 2, instantiation.getArguments().size() ); assertThat( instantiation.getArguments().get( 0 ).getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); assertThat( instantiation.getArguments().get( 1 ).getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); } @Test public void testSimpleDynamicMapInstantiation() { SqmSelectStatement statement = interpretSelect( "select new map(p.nickName as nn, p.numberOfToes as nt) from Person p" ); assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( SqmDynamicInstantiation.class ) ); SqmDynamicInstantiation instantiation = (SqmDynamicInstantiation) statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(); assertThat( instantiation.getInstantiationTarget().getNature(), equalTo( SqmDynamicInstantiationTarget.Nature.MAP ) ); assertThat( instantiation.getInstantiationTarget().getJavaType(), CoreMatchers.<Class>equalTo( Map.class ) ); assertEquals( 2, instantiation.getArguments().size() ); assertThat( instantiation.getArguments().get( 0 ).getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); assertThat( instantiation.getArguments().get( 1 ).getExpression(), instanceOf( SqmSingularAttributeReference.class ) ); } @Test public void testBinaryArithmeticExpression() { final String query = "select p.numberOfToes + p.numberOfToes as b from Person p"; final SqmSelectStatement selectStatement = interpretSelect( query ); final SqmQuerySpec querySpec = selectStatement.getQuerySpec(); final SqmSelection selection = querySpec.getSelectClause().getSelections().get( 0 ); assertThat( querySpec.getFromClause().getFromElementSpaces().size(), is(1) ); final SqmFromElementSpace fromElementSpace = querySpec.getFromClause().getFromElementSpaces().get( 0 ); final SqmRoot root = fromElementSpace.getRoot(); assertThat( root.getBinding().getReferencedNavigable().getEntityName(), endsWith( "Person" ) ); assertThat( fromElementSpace.getJoins().size(), is(0) ); BinaryArithmeticSqmExpression expression = (BinaryArithmeticSqmExpression) selection.getExpression(); SqmSingularAttributeReference leftHandOperand = (SqmSingularAttributeReference) expression.getLeftHandOperand(); assertThat( leftHandOperand.getSourceReference().getExportedFromElement(), sameInstance( root ) ); assertThat( leftHandOperand.getReferencedNavigable().getAttributeName(), is( "numberOfToes" ) ); // assertThat( leftHandOperand.getFromElement(), nullValue() ); SqmSingularAttributeReference rightHandOperand = (SqmSingularAttributeReference) expression.getRightHandOperand(); assertThat( rightHandOperand.getSourceReference().getExportedFromElement(), sameInstance( root ) ); assertThat( rightHandOperand.getReferencedNavigable().getAttributeName(), is( "numberOfToes" ) ); // assertThat( leftHandOperand.getFromElement(), nullValue() ); } @Test public void testBinaryArithmeticExpressionWithMultipleFromSpaces() { final String query = "select p.numberOfToes + p2.numberOfToes as b from Person p, Person p2"; final SqmSelectStatement selectStatement = interpretSelect( query ); final SqmQuerySpec querySpec = selectStatement.getQuerySpec(); final SqmSelection selection = querySpec.getSelectClause().getSelections().get( 0 ); assertThat( querySpec.getFromClause().getFromElementSpaces().size(), is(2) ); final SqmRoot entityRoot = querySpec.getFromClause().getFromElementSpaces().get( 0 ).getRoot(); assertThat( entityRoot.getBinding().getReferencedNavigable().getEntityName(), endsWith( "Person" ) ); final SqmRoot entity2Root = querySpec.getFromClause().getFromElementSpaces().get( 1 ).getRoot(); assertThat( entity2Root.getBinding().getReferencedNavigable().getEntityName(), endsWith( "Person" ) ); BinaryArithmeticSqmExpression addExpression = (BinaryArithmeticSqmExpression) selection.getExpression(); SqmSingularAttributeReference leftHandOperand = (SqmSingularAttributeReference) addExpression.getLeftHandOperand(); assertThat( leftHandOperand.getSourceReference().getExportedFromElement(), sameInstance( entityRoot ) ); assertThat( leftHandOperand.getReferencedNavigable().getAttributeName(), is( "numberOfToes" ) ); assertThat( FromElementHelper.extractExpressionExportedFromElement( leftHandOperand ), nullValue() ); SqmSingularAttributeReference rightHandOperand = (SqmSingularAttributeReference) addExpression.getRightHandOperand(); assertThat( rightHandOperand.getSourceReference().getExportedFromElement(), sameInstance( entity2Root ) ); assertThat( rightHandOperand.getReferencedNavigable().getAttributeName(), is( "numberOfToes" ) ); assertThat( FromElementHelper.extractExpressionExportedFromElement( rightHandOperand ), nullValue() ); } @Test public void testMapKeyFunction() { collectionIndexFunctionAssertions( interpretSelect( "select key(m) from EntityOfMaps e join e.basicToBasicMap m" ), CollectionClassification.MAP, IndexClassification.BASIC, "m" ); collectionIndexFunctionAssertions( interpretSelect( "select key(m) from EntityOfMaps e join e.componentToBasicMap m" ), CollectionClassification.MAP, IndexClassification.EMBEDDABLE, "m" ); } private void collectionIndexFunctionAssertions( SqmSelectStatement statement, CollectionClassification collectionClassification, IndexClassification indexClassification, String collectionAlias) { assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( AbstractSqmCollectionIndexReference.class ) ); final AbstractSqmCollectionIndexReference mapKeyPathExpression = (AbstractSqmCollectionIndexReference) statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(); final SqmPluralAttribute attrRef = mapKeyPathExpression.getPluralAttributeBinding().getReferencedNavigable(); assertThat( attrRef.getCollectionClassification(), is(collectionClassification) ); assertThat( attrRef.getIndexDescriptor().getClassification(), is( indexClassification) ); assertThat( mapKeyPathExpression.getExpressionType(), sameInstance( attrRef.getIndexDescriptor() ) ); assertThat( FromElementHelper.extractExpressionExportedFromElement( mapKeyPathExpression.getPluralAttributeBinding() ).getIdentificationVariable(), is(collectionAlias) ); } @Test public void testMapValueFunction() { collectionValueFunctionAssertions( interpretSelect( "select value(m) from EntityOfMaps e join e.basicToBasicMap m" ), CollectionClassification.MAP, ElementClassification.BASIC, "m" ); collectionValueFunctionAssertions( interpretSelect( "select value(m) from EntityOfMaps e join e.basicToComponentMap m" ), CollectionClassification.MAP, ElementClassification.EMBEDDABLE, "m" ); collectionValueFunctionAssertions( interpretSelect( "select value(m) from EntityOfMaps e join e.basicToOneToMany m" ), CollectionClassification.MAP, ElementClassification.ONE_TO_MANY, "m" ); } @Test public void testCollectionValueFunction() { collectionValueFunctionAssertions( interpretSelect( "select value(b) from EntityOfLists e join e.listOfBasics b" ), CollectionClassification.LIST, ElementClassification.BASIC, "b" ); collectionValueFunctionAssertions( interpretSelect( "select value(b) from EntityOfLists e join e.listOfComponents b" ), CollectionClassification.LIST, ElementClassification.EMBEDDABLE, "b" ); collectionValueFunctionAssertions( interpretSelect( "select value(b) from EntityOfLists e join e.listOfOneToMany b" ), CollectionClassification.LIST, ElementClassification.ONE_TO_MANY, "b" ); // todo : ManyToMany not properly handled atm collectionValueFunctionAssertions( interpretSelect( "select value(b) from EntityOfSets e join e.setOfBasics b" ), CollectionClassification.SET, ElementClassification.BASIC, "b" ); collectionValueFunctionAssertions( interpretSelect( "select value(b) from EntityOfSets e join e.setOfComponents b" ), CollectionClassification.SET, ElementClassification.EMBEDDABLE, "b" ); collectionValueFunctionAssertions( interpretSelect( "select value(b) from EntityOfSets e join e.setOfOneToMany b" ), CollectionClassification.SET, ElementClassification.ONE_TO_MANY, "b" ); // todo : ManyToMany not properly handled atm collectionValueFunctionAssertions( interpretSelect( "select value(b) from EntityOfMaps e join e.basicToBasicMap b" ), CollectionClassification.MAP, ElementClassification.BASIC, "b" ); collectionValueFunctionAssertions( interpretSelect( "select value(b) from EntityOfMaps e join e.basicToComponentMap b" ), CollectionClassification.MAP, ElementClassification.EMBEDDABLE, "b" ); collectionValueFunctionAssertions( interpretSelect( "select value(b) from EntityOfMaps e join e.basicToOneToMany b" ), CollectionClassification.MAP, ElementClassification.ONE_TO_MANY, "b" ); // todo : ManyToMany not properly handled atm } private void collectionValueFunctionAssertions( SqmSelectStatement statement, CollectionClassification collectionClassification, ElementClassification elementClassification, String collectionIdentificationVariable) { assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( SqmCollectionElementReference.class ) ); final SqmCollectionElementReference elementBinding = (SqmCollectionElementReference) statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(); final SqmPluralAttributeReference attrRef = elementBinding.getSourceReference(); assertThat( attrRef.getReferencedNavigable().getCollectionClassification(), is( collectionClassification) ); // assertThat( elementBinding.getExpressionType(), sameInstance( attrRef.getElementDescriptor().getType() ) ); assertThat( attrRef.getReferencedNavigable().getElementDescriptor().getClassification(), is( elementClassification ) ); assertThat( attrRef.getExportedFromElement().getIdentificationVariable(), is( collectionIdentificationVariable ) ); } @Test public void testCollectionValueFunctionNotSupportedInStrictMode() { consumerContext.enableStrictJpaCompliance(); expectedException.expect( StrictJpaComplianceViolation.class ); expectedException.expectMessage( "Encountered application of value() function to path expression which does not resolve to a persistent Map" ); interpretSelect( "select value(b) from EntityOfLists e join e.listOfBasics b" ); } @Test public void testMapEntryFunction() { SqmSelectStatement statement = interpretSelect( "select entry(m) from EntityOfMaps e join e.basicToManyToMany m" ); assertEquals( 1, statement.getQuerySpec().getSelectClause().getSelections().size() ); assertThat( statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(), instanceOf( SqmMapEntryBinding.class ) ); final SqmMapEntryBinding mapEntryFunction = (SqmMapEntryBinding) statement.getQuerySpec().getSelectClause().getSelections().get( 0 ).getExpression(); assertThat( mapEntryFunction.getAttributeBinding().getExportedFromElement(), notNullValue() ); assertThat( mapEntryFunction.getAttributeBinding().getExportedFromElement().getIdentificationVariable(), is( "m") ); final SqmPluralAttribute attrRef = mapEntryFunction.getAttributeBinding().getReferencedNavigable(); assertThat( attrRef.getCollectionClassification(), is(CollectionClassification.MAP) ); // Key assertThat( attrRef.getIndexDescriptor().getClassification(), is( IndexClassification.BASIC) ); assertEquals( String.class, attrRef.getIndexDescriptor().getExportedDomainType().getJavaType() ); // value/element assertThat( attrRef.getElementDescriptor().getClassification(), is( ElementClassification.ONE_TO_MANY) ); assertThat( ( (SqmExpressableTypeEntity) attrRef.getElementDescriptor() ).getEntityName(), is( "org.hibernate.sqm.test.domain.EntityOfMaps" ) ); assertEquals( EntityOfMaps.class, attrRef.getElementDescriptor().getExportedDomainType().getJavaType() ); } public static class DTO { } }